/**
 * @zh 默认的旋转四元组
 * @en Default quaternion
 */
const Quat = new GameQuaternion(0, 0, 0, 1);

/**
 * @zh 动物管理类
 * @en Animal manager
 */
class AnimalManager {
    /**
     * @zh 实体对象
     * @en Entity object
     */
    public entity: GameEntity;

    /**
     * @zh Interval记录器
     * @en Interval recorder
     */
    private interval: number;

    /**
     * @zh Tick时间
     * @en Tick time
     */
    private tick: number;

    /**
     * @zh 旋转tick时间
     * @en Rotation tick time
     */
    private rotTick: number;

    /**
     * @zh 旋转值
     * @en Rotation value
     */
    private rot: number;

    /**
     * @zh 旋转速度
     * @en Rotation speed
     */
    private rotSpeed: number;

    /**
     * @zh 巡逻速度
     * @en Patrol speed
     */
    public patrolSpeed: number;

    /**
     * @zh 巡逻tick时间
     * @en Patrol tick time
     */
    private speedTick: number;

    /**
     * @zh 是否待机
     * @en Check if in IDLE
     */
    public inIdle: boolean;

    /**
     * @zh 是否死亡
     * @en Check if is dead
     */
    public isDead: boolean;

    /**
     * @zh 自身旋转四元组
     * @en Self quaternion
     */
    private selfQuat: GameQuaternion;

    /**
     * @zh 寻找半径
     * @en Find range
     */
    public findRange: number;

    /**
     * @zh 追踪速度
     * @en Trace speed
     */
    public traceSpeed: number;

    /**
     * @zh 可否飞行
     * @en Check if can fly
     */
    public canFly: boolean;

    /**
     * @zh 进攻消息
     * @en Attack message
     */
    public message: string | null;

    /**
     * @zh 进攻音频
     * @en Attack audio
     */
    public audio: GameAudioAssets | null;

    /**
     * @zh 初始化动物管理类
     * @param entity 实体
     * @param message 进攻消息
     * @param audio 进攻音频
     * @param patrolSpeed 巡逻速度
     * @param findRange 查找半径
     * @param traceSpeed 追踪速度
     * @param canFly 可否飞行
     * @en Initialize animal manager
     * @param entity Entity
     * @param message Attack message
     * @param audio Attack audio
     * @param patrolSpeed Patrol speed
     * @param findRange Find range radius
     * @param traceSpeed Trace speed
     * @param canFly Check if can fly
     */
    public constructor(
        entity: GameEntity,
        message: string | null = null,
        audio: GameAudioAssets | null = null,
        patrolSpeed: number = 0.1,
        findRange: number = 10,
        traceSpeed: number = 0.5,
        canFly: boolean = false
    ) {
        this.entity = entity;
        this.entity.collides = true;
        this.entity.gravity = true;
        this.entity.addTag('animal');
        this.message = message;
        this.audio = audio;
        this.rot = 0;
        this.rotSpeed = 1;
        this.tick = 0;
        this.rotTick = 0;
        this.patrolSpeed = patrolSpeed;
        this.speedTick = 0;
        this.inIdle = false;
        this.isDead = false;
        this.selfQuat = Quat;
        this.findRange = findRange;
        this.traceSpeed = traceSpeed;
        this.canFly = canFly;
        this.interval = setInterval(() => {
            this.tick++;
            this.onTick();
        }, 60);
    }

    /**
     * @zh 创建`AnimalManager`实例
     * @param entity 实体
     * @param message 进攻消息
     * @param audio 进攻音频
     * @param patrolSpeed 巡逻速度
     * @param findRange 查找半径
     * @param traceSpeed 追踪速度
     * @param canFly 可否飞行
     * @returns 返回`AnimalManager`实例对象
     * @en Create `AnimalManager` instance
     * @param entity Entity
     * @param message Attack message
     * @param audio Attack audio
     * @param patrolSpeed Patrol speed
     * @param findRange Find range radius
     * @param traceSpeed Trace speed
     * @param canFly Check if can fly
     * @returns Returns `AnimalManager` instance object
     */
    public static create(
        entity: GameEntity,
        message?: string | null,
        audio?: GameAudioAssets | null,
        patrolSpeed?: number,
        findRange?: number,
        traceSpeed?: number,
        canFly?: boolean
    ) {
        return new AnimalManager(
            entity,
            message,
            audio,
            patrolSpeed,
            findRange,
            traceSpeed,
            canFly
        );
    }

    /**
     * @zh 获取Interval记录器
     * @returns 返回Interval对象
     * @en Get interval recorder
     * @returns Returns interval recorder
     */
    public getInterval(): number {
        return this.interval;
    }

    /**
     * @zh 持续tick时间
     * @returns 范围内有无玩家（如果实体对象已死亡，则返回`null`）
     * @en Continually tick time
     * @returns Check if has player in the range
     */
    protected onTick(): boolean | null {
        if (this.isDead) {
            return null;
        }
        const players = this.getNearbyPlayers(this.findRange);
        if (players.length > 0) {
            this.turnToFight();
            this.moveToTarget(players[0].position);
            if (this.message !== null) {
                this.entity.say(this.message);
            }
            players.forEach((player) => {
                if (this.audio !== null) {
                    player.player.sound(this.audio);
                }
            });
            return true;
        } else {
            this.turnToIdle();
            this.animateNoAim();
            return false;
        }
    }

    /**
     * @zh 转变至待机状态
     * @en Turn to IDLE status
     */
    protected turnToIdle(): void {
        this.inIdle = true;
    }

    /**
     * @zh 转变至攻击状态
     * @en Turn to FIGHT status
     */
    protected turnToFight(): void {
        this.inIdle = false;
    }

    /**
     * @zh 无目标时进行巡逻
     * @en Patrol when there is no target
     */
    protected animateNoAim(): void {
        if (this.tick >= this.rotTick) {
            this.rotSpeed = Math.PI * (Math.random() - 0.5) * 0.25;
            this.rotTick = this.tick + this.randomRange(2, 4) * 16;
        }
        if (this.tick >= this.speedTick) {
            this.patrolSpeed = Math.random() * 0.3;
            this.speedTick = this.tick + this.randomRange(3, 5) * 16;
        }
        this.rot += this.rotSpeed * this.patrolSpeed;
        const vx = Math.cos(this.rot) * this.patrolSpeed;
        const vz = Math.sin(this.rot) * this.patrolSpeed;
        this.entity.velocity.x = vx;
        this.entity.velocity.z = vz;
        this.entity.meshOrientation = this.selfQuat.rotateY(Math.atan2(vz, vx));
    }

    /**
     * @zh 像目标处移动
     * @param pos 目标坐标
     * @en Move to target
     * @param pos The position of target
     */
    moveToTarget(pos: GameVector3) {
        const vel = pos.sub(this.entity.position).normalize();
        if (!this.canFly) {
            vel.y = 0;
        }
        this.entity.velocity.x = vel.x * this.traceSpeed;
        this.entity.velocity.y = vel.y * this.traceSpeed;
        this.entity.velocity.z = vel.z * this.traceSpeed;
        const origin = this.getOrientationByVector(this.selfQuat, vel);
        this.entity.meshOrientation = this.entity.meshOrientation.slerp(
            origin,
            0.24
        );
    }

    /**
     * @zh 寻找附近的玩家
     * @param range 范围半径
     * @returns 玩家列表
     * @en Get near by players
     * @param range Range radius
     * @returns Players list
     */
    getNearbyPlayers(range: number): GamePlayerEntity[] {
        let players = world.querySelectorAll('player');
        const pos = this.entity.position;
        players = players.filter((e) => e.position.distance(pos) < range);
        players.sort(
            (a, b) => a.position.distance(pos) - b.position.distance(pos)
        );
        return players;
    }

    /**
     * @zh 区域内随机数
     * @param start 起点
     * @param end 终点
     * @returns 随机数
     * @en Random number in the range
     * @param start Start number
     * @param end End number
     * @returns Random number
     */
    randomRange(start: number, end: number): number {
        return start + (end - start) * Math.random();
    }

    /**
     * @zh 通过向量计算角度
     * @param quat 旋转四元组
     * @param vector3 三维向量
     * @returns 旋转四元组
     * @en Calculate degrees based on vectors
     * @param quat Quaternion
     * @param vector3 Vector 3
     * @returns Quaternion
     */
    getOrientationByVector(
        quat: GameQuaternion,
        vector3: GameVector3
    ): GameQuaternion {
        const src = vector3;
        const dx = src.x;
        const dy = src.y;
        const dz = src.z;
        const dist = Math.sqrt(dx * dx + dz * dz);
        const rotX = Math.atan2(dy, dist);
        return quat.rotateX(rotX).rotateY(Math.atan2(dz, dx));
    }
}

// 导出模块 Export modules
export { AnimalManager };
